﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArchMethodLibrary.Utils;

namespace WallCreateAndEdit.WallModule.EditWall
{
	public class WallEditor
	{
		public WallEditor(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_Options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
		}

		public bool OffsetCurve(Wall wall, XYZ offsetDirection, ref Curve offsetedCurve)
		{
			LocationCurve locationCurve = wall.Location as LocationCurve;
			Curve curve = locationCurve.Curve;
			if (curve.GetType() == typeof(Line))
			{
				Line curve2 = curve as Line;
				XYZ xyz = curve2.GetEndPoint(0);
				XYZ xyz2 = curve2.GetEndPoint(1);
				xyz += offsetDirection;
				xyz2 += offsetDirection;
				offsetedCurve = YJKLineEx.YJKGetBound(xyz, xyz2);
			}
			else
			{
				if (!(curve.GetType() == typeof(Arc)))
				{
					return false;
				}
				Arc arc = curve as Arc;
				XYZ xyz3 = arc.GetEndPoint(0);
				XYZ xyz4 = arc.GetEndPoint(1);
				XYZ center = arc.Center;
				XYZ xyz5 = Geometry.CalculatMidPoint(xyz3, xyz4, center, arc.Normal);
				XYZ xyz6 = (xyz5 - center).Normalize();
				double num = offsetDirection.GetLength();
				double angle = Geometry.getAngle(XYZ.Zero, xyz6);
				double angle2 = Geometry.getAngle(XYZ.Zero, offsetDirection);
				double val = Geometry.IntersectionAngle(angle, angle2);
				if (Geometry.GreaterThan(val, Math.PI*.5))
				{
					num = -num;
				}
				XYZ xyz7 = xyz6 * num;
				xyz5 += xyz7;
				xyz7 = (xyz3 - center).Normalize() * num;
				xyz3 += xyz7;
				xyz7 = (xyz4 - center).Normalize() * num;
				xyz4 += xyz7;
				offsetedCurve = this.m_Revit.Application.ActiveUIDocument.Document.CreatYJKArc(xyz3, xyz4, xyz5);
			}
			return true;
		}

		public bool Offset(Wall wall, XYZ offsetDirection, XYZ newStartPoint, XYZ newEndPoint)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			XYZ xyz = new XYZ(10000.0, 10000.0, 0.0);
			List<Element> subElements = this.GetSubElements(this.m_Revit, wall);
			List<ParasiticElementData> list = new List<ParasiticElementData>();
			LocationCurve locationCurve = wall.Location as LocationCurve;
			Curve curve = locationCurve.Curve;
			Curve curve2 = null;
			if (curve.GetType() == typeof(Line))
			{
				curve2 = YJKLineEx.YJKGetBound(newStartPoint + xyz, newEndPoint + xyz);
				using (List<Element>.Enumerator enumerator = subElements.GetEnumerator())
				{
					while (enumerator.MoveNext())
					{
						Element element = enumerator.Current;
						list.Add(new ParasiticElementData(element, xyz + offsetDirection));
					}
					goto IL_242;
				}
			}
			if (curve.GetType() == typeof(Arc))
			{
				Arc arc = curve as Arc;
				XYZ hyendPoint = arc.GetEndPoint(0);
				XYZ hyendPoint2 = arc.GetEndPoint(1);
				XYZ center = arc.Center;
				XYZ xyz2 = Geometry.CalculatMidPoint(hyendPoint, hyendPoint2, center, arc.Normal);
				XYZ xyz3 = (xyz2 - center).Normalize();
				double num = offsetDirection.GetLength();
				double angle = Geometry.getAngle(XYZ.Zero, xyz3);
				double angle2 = Geometry.getAngle(XYZ.Zero, offsetDirection);
				double val = Geometry.IntersectionAngle(angle, angle2);
				if (Geometry.GreaterThan(val, Math.PI*.5))
				{
					num = -num;
				}
				XYZ xyz4 = xyz3 * num;
				xyz2 += xyz + xyz4;
				curve2 = this.m_Revit.Application.ActiveUIDocument.Document.CreatYJKArc(newStartPoint + xyz, newEndPoint + xyz, xyz2);
				using (List<Element>.Enumerator enumerator2 = subElements.GetEnumerator())
				{
					while (enumerator2.MoveNext())
					{
						Element element2 = enumerator2.Current;
						FamilyInstance familyInstance = element2 as FamilyInstance;
						LocationPoint locationPoint = familyInstance.Location as LocationPoint;
						XYZ point = locationPoint.Point;
						xyz4 = (point - center).Normalize() * num;
						list.Add(new ParasiticElementData(element2, xyz + xyz4));
					}
					goto IL_242;
				}
			}
			return false;
			IL_242:
			WallType wallType = wall.WallType;
			Level hylevel = wall.GetYJKLevel();
			double num2 = AssistFunc.mmToFeet(4000.0);
			double num3 = 0.0;
			Wall wall2 = Wall.Create(document, curve2, wallType.Id, hylevel.Id, num2, num3, false, false);
			if (wall.Flipped)
			{
				wall2.Flip();
			}
			this.CopyParametersOfWall(wall, ref wall2);
			foreach (ParasiticElementData parasiticElementData in list)
			{
				Element theElement = parasiticElementData.TheElement;
				List<ElementId> list2 = new List<ElementId>();
				this.TransformParasiticElement(theElement, wall2, parasiticElementData.Offset, ref list2);
			}
			document.DeleteElement(wall);
			ElementTransformUtils.MoveElement(document, wall2.Id, -xyz);
			return true;
		}

		private void CopyParametersOfWall(Wall wall, ref Wall newWall)
		{
			foreach (object obj in wall.Parameters)
			{
				Parameter parameter = (Parameter)obj;
				if (!parameter.IsReadOnly && parameter.HasValue)
				{
					try
					{
                        switch ((int)parameter.StorageType)
						{
						case 1:
							newWall.get_Parameter(parameter.Definition).Set(parameter.AsInteger());
							break;
						case 2:
							newWall.get_Parameter(parameter.Definition).Set(parameter.AsDouble());
							break;
						case 3:
							newWall.get_Parameter(parameter.Definition).Set(parameter.AsString());
							break;
						case 4:
							newWall.get_Parameter(parameter.Definition).Set(parameter.AsElementId());
							break;
						}
					}
					catch (Exception)
					{
					}
				}
			}
		}

		private List<Element> GetSubElements(ExternalCommandData cmdData, Element hostObject)
		{
			Document document = cmdData.Application.ActiveUIDocument.Document;
			List<Element> list = new List<Element>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(cmdData.Application.ActiveUIDocument.Document, cmdData.Application.ActiveUIDocument.ActiveView.Id);
			ICollection<ElementId> collection = filteredElementCollector.ToElementIds();
			foreach (ElementId elementId in collection)
			{
				Element element = document.GetElement(elementId);
				FamilyInstance familyInstance = element as FamilyInstance;
				if (familyInstance != null && familyInstance.Host != null && familyInstance.Host.Id.IntegerValue.Equals(hostObject.Id.IntegerValue))
				{
					list.Add(element);
				}
			}
			return list;
		}

		private bool TransformParasiticElement(Element element, Element newHostObj, XYZ move, ref List<ElementId> newElementsId)
		{
			FamilyInstance familyInstance = element as FamilyInstance;
			LocationPoint locationPoint = element.Location as LocationPoint;
			if (locationPoint == null)
			{
				return false;
			}
			XYZ xyz = locationPoint.Point + move;
			Type type = element.GetType();
			if (!(type == typeof(FamilyInstance)))
			{
				return false;
			}
			FamilySymbol symbol = familyInstance.Symbol;
			if (symbol == null)
			{
				return false;
			}
			FamilyInstance familyInstance2 = null;
			try
			{
				Parameter parameter = familyInstance.Symbol.Family.get_Parameter(BuiltInParameter.FAMILY_HOSTING_BEHAVIOR);
				if (parameter == null)
				{
					return false;
				}
				int num = parameter.AsInteger();
				if (num != 5)
				{
					ElementId elementId = familyInstance.get_Parameter(BuiltInParameter.FAMILY_LEVEL_PARAM).AsElementId();
					Level level = this.m_Revit.Application.ActiveUIDocument.Document.GetElement(elementId) as Level;
					UnifiedModified.ActivateFamilySymbol(symbol);
					familyInstance2 = this.m_Revit.Application.ActiveUIDocument.Document.Create.NewFamilyInstance(xyz, symbol, newHostObj, level, familyInstance.StructuralType);
					this.SetInsertDirection(familyInstance, ref familyInstance2);
				}
				else
				{
					XYZ dir = familyInstance.HandOrientation.CrossProduct(familyInstance.FacingOrientation);
					Reference referenceFaceOfHost = this.GetReferenceFaceOfHost(newHostObj, xyz, dir);
					XYZ handOrientation = familyInstance.HandOrientation;
					UnifiedModified.ActivateFamilySymbol(symbol);
					familyInstance2 = this.m_Revit.Application.ActiveUIDocument.Document.Create.NewFamilyInstance(referenceFaceOfHost, xyz, handOrientation, symbol);
				}
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				return false;
			}
			if (familyInstance2 == null)
			{
				return false;
			}
			this.CopyParameters(familyInstance, ref familyInstance2);
			newElementsId.Add(familyInstance2.Id);
			return true;
		}

		private void SetInsertDirection(FamilyInstance instance, ref FamilyInstance newInstance)
		{
			if (instance.FacingFlipped != newInstance.FacingFlipped)
			{
				newInstance.flipFacing();
			}
			if (instance.HandFlipped != newInstance.HandFlipped)
			{
				newInstance.flipHand();
			}
		}

		private void CopyParameters(FamilyInstance element, ref FamilyInstance newElement)
		{
			foreach (object obj in element.Parameters)
			{
				Parameter parameter = (Parameter)obj;
				if (!parameter.IsReadOnly && parameter.HasValue)
				{
					try
					{
                        switch ((int)parameter.StorageType)
						{
						case 1:
							newElement.get_Parameter(parameter.Definition).Set(parameter.AsInteger());
							break;
						case 2:
							newElement.get_Parameter(parameter.Definition).Set(parameter.AsDouble());
							break;
						case 3:
							newElement.get_Parameter(parameter.Definition).Set(parameter.AsString());
							break;
						}
					}
					catch (Exception)
					{
					}
				}
			}
		}

		private Reference GetReferenceFaceOfHost(Element host, XYZ pos, XYZ dir)
		{
			GeometryElement geometryElement = host.get_Geometry(this.m_Options);
			IEnumerator<GeometryObject> enumerator = geometryElement.GetEnumerator();
			List<GeometryObject> list = new List<GeometryObject>();
			while (enumerator.MoveNext())
			{
				GeometryObject item = enumerator.Current;
				list.Add(item);
			}
			double val = 0.0;
			Reference result = null;
			foreach (GeometryObject geometryObject in list)
			{
				Solid solid = geometryObject as Solid;
				if (!(solid == null))
				{
					FaceArray faces = solid.Faces;
					foreach (object obj in faces)
					{
						Face face = (Face)obj;
						if (!(face.GetType() != typeof(PlanarFace)))
						{
							PlanarFace planarFace = face as PlanarFace;
							IntersectionResult intersectionResult = planarFace.Project(pos);
							if (intersectionResult != null && (planarFace.FaceNormal().IsAlmostEqualTo(dir) || planarFace.FaceNormal().IsAlmostEqualTo(-dir)))
							{
								if (Geometry.IsEqual(intersectionResult.Distance, 0.0))
								{
									return planarFace.Reference;
								}
								if (Geometry.Lessthan_Or_Equal(intersectionResult.Distance, val))
								{
									val = intersectionResult.Distance;
									result = planarFace.Reference;
								}
							}
						}
					}
				}
			}
			return result;
		}

		private void UpdateModel(UIDocument uiDoc, bool autoJoin)
		{
			uiDoc.Document.Regenerate();
			if (autoJoin)
			{
				uiDoc.Document.AutoJoinElements();
			}
			uiDoc.RefreshActiveView();
		}

		private ExternalCommandData m_Revit;

		private Options m_Options;
	}
}
